#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>

#include <fcntl.h>
#include <errno.h>
#include <linux/input.h>
#include <linux/joystick.h>

#include <vector>
#include <string>

#define XBOX_TYPE_BUTTON    0x01
#define XBOX_TYPE_AXIS      0x02

struct stick_btn_t
{
    const char *name;
    int8_t idx;
    int8_t type;
    int16_t value;
};

std::vector<stick_btn_t> stick_btns = {
    {
        .name = "A",
        .idx = 0,
        .type = JS_EVENT_BUTTON,
    },
    {
        .name = "B",
        .idx = 1,
        .type = JS_EVENT_BUTTON,
    },
    {
        .name = "X",
        .idx = 3,
        .type = JS_EVENT_BUTTON,
    },
    {
        .name = "Y",
        .idx = 4,
        .type = JS_EVENT_BUTTON,
    },
    {
        .name = "start",
        .idx = 11,
        .type = JS_EVENT_BUTTON,
    },
    {
        .name = "left-axis-x",
        .idx = 0,
        .type = JS_EVENT_AXIS,
    },
    {
        .name = "left-axis-y",
        .idx = 1,
        .type = JS_EVENT_AXIS,
    },
    {
        .name = "right-axis-x",
        .idx = 2,
        .type = JS_EVENT_AXIS,
    },
    {
        .name = "right-axis-y",
        .idx = 3,
        .type = JS_EVENT_AXIS,
    },
    {
        .name = "axis-left-right",
        .idx = 6,
        .type = JS_EVENT_AXIS,
    },
    {
        .name="axis-up-down",
        .idx = 7,
        .type = JS_EVENT_AXIS,
    }
};


int stick_open(const char *file_name)
{
    int fd = open(file_name, O_RDONLY);
    if (fd < 0)
    {
        perror("open");
        return -1;
    }

    return fd;
}
 
int stick_map_read(int stick_fd, std::vector<stick_btn_t> &stick_btns)
{
    struct js_event js;

    int len = read(stick_fd, &js, sizeof(struct js_event));
    if (len < 0)
    {
        //perror("read");
        return -1;
    }


    for (auto &iter: stick_btns)
    {
        if (iter.idx == js.number && iter.type == js.type)
        {
            iter.value = js.value;
            printf("type%d, #%d, value=%d\n", js.type, js.number, js.value);
            break;
        }
    }

    return len;
}

void stick_close(int stick_fd)
{
    close(stick_fd);
    return;
}


int init_broadcast()
{
    struct sockaddr_in addr;
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd == -1)
    {
        printf("socket fail\n");
        return -1;
    }

    int opt = 0;
    if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST | SO_REUSEADDR, &opt, sizeof opt) < 0)
    {
        printf("setsockopt failed!");
    }
    return fd;
}

void send_broadcast(int fd, unsigned short port, const std::vector<stick_btn_t> &stick_btns)
{
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(struct sockaddr_in));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    addr.sin_port = htons(port);


    static char send_buffer[1024];
    int len = 0;
    // | ( | time | btn_cnt | type | idx | value | ) |
    // | ( |   4  |    1    |   1  |  1  |   2   | ) |
    send_buffer[len++] = '(';
    uint32_t time_now = time(NULL);
    memcpy(send_buffer + len, &time_now, sizeof(time_now));
    len += sizeof(time_now);
    send_buffer[len++] = (uint8_t)stick_btns.size();

    for (auto &iter: stick_btns)
    {
        send_buffer[len++] = iter.type;
        send_buffer[len++] = iter.idx;
        memcpy(send_buffer+ len, &(iter.value), sizeof(uint16_t));
        len += sizeof(uint16_t);
    }
    send_buffer[len++] = ')';

    int send_bytes = sendto(fd, send_buffer, len, 0, (struct sockaddr*)&addr, sizeof(struct sockaddr));
    if (send_bytes != len)
    {
        fprintf(stderr, "send broadcast failed");
        perror("open");
    }

}

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        fprintf(stderr, "args error, need StickService UDP port\n");
        return 0;
    }

    int port = atoi(argv[1]);
    int stick_fd = stick_open("/dev/input/js0");
    if(stick_fd < 0)
    {
        return -1;
    }
    fcntl(stick_fd, F_SETFL, O_NONBLOCK);


    int sock_fd = init_broadcast();

    while(1)
    {
        int len = stick_map_read(stick_fd, stick_btns);
        if (len < 0)
        {
            //usleep(10*1000);
            //printf("x");
            //fflush(stdout);
            continue;
        }
        send_broadcast(sock_fd, port, stick_btns);
        usleep(100*1000);
    }

    stick_close(stick_fd);
    return 0;
}
